
/*
 * (a)  (C) 1990 by Adobe Systems Incorporated. All rights reserved.
 *
 * (b)  If this Sample Code is distributed as part of the Display PostScript
 *	System Software Development Kit from Adobe Systems Incorporated,
 *	then this copy is designated as Development Software and its use is
 *	subject to the terms of the License Agreement attached to such Kit.
 *
 * (c)  If this Sample Code is distributed independently, then the following
 *	terms apply:
 *
 * (d)  This file may be freely copied and redistributed as long as:
 *	1) Parts (a), (d), (e) and (f) continue to be included in the file,
 *	2) If the file has been modified in any way, a notice of such
 *      modification is conspicuously indicated.
 *
 * (e)  PostScript, Display PostScript, and Adobe are registered trademarks of
 *	Adobe Systems Incorporated.
 * 
 * (f) THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO
 *	CHANGE WITHOUT NOTICE, AND SHOULD NOT BE CONSTRUED
 *	AS A COMMITMENT BY ADOBE SYSTEMS INCORPORATED.
 *	ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY
 *	OR LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO
 *	WARRANTY OF ANY KIND (EXPRESS, IMPLIED OR STATUTORY)
 *	WITH RESPECT TO THIS INFORMATION, AND EXPRESSLY
 *	DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, 
 *	FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT
 *	OF THIRD PARTY RIGHTS.
 */

/*
 *	DrawingView.m
 *
 *	This view represents the page that the image is drawn onto. It is
 *	a subview of the DocView. The DocView is the document view of
 *	the ClipView. This view's real size grows with the scale but the
 *	bounds always stays the same.
 *
 *
 *	Version:	2.0
 *	Author:	Ken Fromm
 *	History:
 *			03-07-91		Added this comment.
 */

#import "DrawingView.h"
#import "DrawingViewWraps.h"
#import "DocView.h"
#import "Graphic.h"
#import "ScrollApp.h"
#import <objc/List.h>
#import <appkit/Button.h>
#import <appkit/Control.h>
#import <appkit/Matrix.h>
#import <appkit/nextstd.h>
#import <dpsclient/dpsclient.h>
#import <dpsclient/wraps.h>

extern void initGparms();
extern void setGparms();

@implementation DrawingView

/*
*	Allocate a gstate, set the clipping to NO because it will be clipped
*	by the clip view.
*/
+newFrame:(NXRect *) frm
{	
	self = [super newFrame:frm]; 
	
	drawUpath = [NXApp  getUpathBuffer];
	drawManner = UPATH;
	
	return self;
}

- free
{
	[self  freeGraphics];

	return [super free];
}

- setFieldsMode:(BOOL) mode
{
	displayFields = eraseFields = mode;

	return self;
}

- setTypeOfDrawing:sender
{
	id		matrixId;

	drawFrame = ([sender selectedRow] == 1);

	matrixId = [NXApp  getStrokingMatrix];
	[[matrixId cellAt:0 :0] setEnabled:drawFrame];
	[[matrixId cellAt:1 :0] setEnabled:drawFrame];

	matrixId = [NXApp  getParameterMatrix];
	[[matrixId cellAt:0 :0] setEnabled:!drawFrame];
	[[matrixId cellAt:1 :0] setEnabled:!drawFrame];

	[NXApp  redrawAction:self];

	return self;
}

- setSelectivity:sender
{
	drawEverything = ([sender selectedRow] == 1);  

	[NXApp  redrawAction:self];

	return self;
}

- setDrawingManner:sender
{
	drawManner = [sender selectedRow];

	[NXApp  redrawAction:self];

	return self;
}

- setStroking:sender
{
	drawCombined = ([sender selectedRow] == 1);

	[NXApp  redrawAction:self];

	return self;
}

- setParameterSetting:sender
{
	drawEveryPath = ([sender selectedRow] == 1);

	[NXApp  redrawAction:self];

	return self;
}

-setDrawOrigin:(NXPoint *)origin
{
	drawOrigin = *origin;
	
	return self;
}

- freeGraphics
{
	if (graphicsListId)
		[graphicsListId  freeObjects];
		
	[graphicsListId free];

	return self;
}

- insertList:listId
{
	[self  freeGraphics];
	graphicsListId = listId;

	return self;
}

- eraseFields:sender
{
	id		matrixId;

	int		i;

	matrixId = [NXApp getTimingMatrix];
	for (i= 0; i < [matrixId cellCount]; i++)
		[[matrixId cellAt:i :0] setStringValue:""];

	matrixId = [NXApp getStatusMatrix];
	for (i= 0; i < [matrixId cellCount]; i++)
		[[matrixId cellAt:i :0] setStringValue:""];

	return self;
}

- displayFields:sender
{
	id		matrixId;

	int		i, ucache_values[5];
	
	 if (timing_info.num_draws)
	{
		matrixId = [NXApp getTimingMatrix];
		[[matrixId cellAt:0 :0]
			setFloatValue:timing_info.num_draws];
		[[matrixId cellAt:1 :0]
			setFloatValue:timing_info.drawingtime/timing_info.num_draws];
		if (!drawFrame)
		{
			[[matrixId cellAt:2 :0]
				setFloatValue:timing_info.num_fills/timing_info.num_draws];
			[[matrixId cellAt:3 :0]
				setFloatValue:timing_info.num_strokes/timing_info.num_draws];
		}
		else
			[[matrixId cellAt:3 :0]
				setFloatValue:timing_info.num_strokes/timing_info.num_draws];
		[[matrixId cellAt:4 :0]
			setFloatValue:timing_info.num_subpaths/timing_info.num_draws];
	}

	if (drawManner == UCACHE)
	{
		matrixId = [NXApp getStatusMatrix];
		PSWUcachestatus(&ucache_values[0], &ucache_values[1],
			&ucache_values[2], &ucache_values[3], &ucache_values[4]);
		for (i= 0; i < [matrixId cellCount]; i++)
			[[matrixId cellAt:i :0] setIntValue:ucache_values[i]];
	}

	/* Prepare for next time around. */
	timing_info.num_fills = timing_info.num_strokes = 0;
	timing_info.num_subpaths = timing_info.num_draws = 0;
	timing_info.drawingtime = 0.0;

	return self;
}

/*
*	If the docview is zooming, then scale the drawing view.
*/
- mouseDown:(NXEvent *)event
{
	NXPoint		p;
	
 	p = event->location; 
	if ([superview  isZooming])
	{
		displayFields = eraseFields = YES;
		return [nextResponder  scaleDrawView:self  toPoint:&p];
	}

	return self;
}

/*
*	Initialize the user path structure. Always include a ucache. 
*	If ucache is not used then start at the second entry in the op
*	array and decrement num_ops by one.
*/
static void initUpath(UPath *aUpath)
{	
	aUpath->ops[0] = dps_ucache;
	aUpath->num_ops = 1;

	aUpath->pts[0] = 99999;
	aUpath->pts[1] = 99999;
	aUpath->pts[2] = -99999;
	aUpath->pts[3] = -99999;
	aUpath->num_pts = 4;
}

/*
*	Increment the stroke count and stroke the existing path.
*/
static void strokeRedBook(int *strokes)
{
	*strokes += 1;
	PSstroke();
}

/*
*	Call the appropriate wrap to construct the path. Not the recommended
*	approach to drawing (user paths are). Made external so Graphic.m
*	can make use of it.
*/
void makeRedBook(UPath *aUpath)
{
	int		i_op, i_pt;
	
	i_pt = 4;

	/* Skip the ucache operator. */
	for (i_op = 1; i_op < aUpath->num_ops; i_op++)
	{
		switch (aUpath->ops[i_op])
		{
			case dps_moveto:
				PSmoveto(aUpath->pts[i_pt], aUpath->pts[i_pt+1]);
				i_pt += 2;
				break;
			case dps_rmoveto:
				PSrmoveto(aUpath->pts[i_pt], aUpath->pts[i_pt+1]);
				i_pt += 2;
				break;
			case dps_lineto:
				PSlineto(aUpath->pts[i_pt], aUpath->pts[i_pt+1]);
				i_pt += 2;
				break;
			case dps_rlineto:
				PSrlineto(aUpath->pts[i_pt], aUpath->pts[i_pt+1]);
				i_pt += 2;
				break;
			case dps_curveto:
				PScurveto(aUpath->pts[i_pt], aUpath->pts[i_pt+1],
					aUpath->pts[i_pt+2], aUpath->pts[i_pt+3],
					aUpath->pts[i_pt+4], aUpath->pts[i_pt+5]);
				i_pt += 6;
				break;
			case dps_rcurveto:
				PSrcurveto(aUpath->pts[i_pt], aUpath->pts[i_pt+1],
					aUpath->pts[i_pt+2], aUpath->pts[i_pt+3],
					aUpath->pts[i_pt+4], aUpath->pts[i_pt+5]);
				i_pt += 6;
				break;
			case dps_closepath:
				PSclosepath();
				break;
		}
	}
}

/*
*	If the ucache is to be used then include the first operator
*	otherwise skip it.
*/
static void strokeUpath(UPath *aUpath, int manner, int *strokes)
{
	*strokes += 1;
	if (manner == UCACHE)
		DPSDoUserPath(&aUpath->pts[4], aUpath->num_pts - 4, dps_float,
			&aUpath->ops[0], aUpath->num_ops, aUpath->pts, dps_ustroke);	
	else
		DPSDoUserPath(&aUpath->pts[4], aUpath->num_pts - 4, dps_float,
			&aUpath->ops[1], aUpath->num_ops-1, aUpath->pts, dps_ustroke);
}

/*
*	Omit the bounding box numbers at the beginning of the operand
*	array as well as the dps_ucache at the beginning of the operator
*	array. Update the bounding box of the user path if necessary.
*/
- getUpath:(NXRect *) r  fromGraphic:graphicId
{
	int		ops_before;
	
	UPath	*gUpath;

	[graphicId  getUpath:&gUpath forRect:r];
	if (gUpath)
	{
		timing_info.num_subpaths++;
		if (!drawCombined)
		{
			if (drawManner == REDBOOK)
			{
				makeRedBook(gUpath);
				strokeRedBook(&timing_info.num_strokes);
			}
			else
				strokeUpath(gUpath, drawManner, &timing_info.num_strokes);
		}
		else
		{
			if (drawUpath->num_pts + gUpath->num_pts > PTS_UPATH_BUFFER ||
	 	 	    drawUpath->num_ops + gUpath->num_ops > OPS_UPATH_BUFFER)
			{
				if (drawManner == REDBOOK)
					strokeRedBook(&timing_info.num_strokes);
				else
					strokeUpath(drawUpath, drawManner, &timing_info.num_strokes);

				initUpath(drawUpath);
			}
			
			if (drawManner == REDBOOK)
				makeRedBook(gUpath);
			else
			{
				bcopy(&gUpath->pts[4], &drawUpath->pts[drawUpath->num_pts],
					(gUpath->num_pts - 4)* (sizeof(float)/sizeof(char)));
				bcopy(&gUpath->ops[1], &drawUpath->ops[drawUpath->num_ops],
					gUpath->num_ops-1);

				drawUpath->pts[0] = MIN(gUpath->pts[0], drawUpath->pts[0]);
				drawUpath->pts[1] = MIN(gUpath->pts[1], drawUpath->pts[1]);
				drawUpath->pts[2] = MAX(gUpath->pts[2], drawUpath->pts[2]);
				drawUpath->pts[3] = MAX(gUpath->pts[3], drawUpath->pts[3]);
			}

			drawUpath->num_pts += gUpath->num_pts-4;
			drawUpath->num_ops += gUpath->num_ops-1;
		}
	}

	return self;
}

/*
*	Compare the bounds of the object with the rectangle to draw in order to
*	eliminate unnecessary drawing. The modal session stuff is just to
*	intercept the selection of the redraw button in the interface during
*	a trace. (This stops the drawing. It's only check during a trace
*	because the trace can be quite long.)
*/
- drawSelf:(NXRect *)r :(int) count
{
	int			i, num, ElapsedTime;

	BOOL		tracing;

	GParms		parms;
	GParms		*p_parms;
	
	NXRect		rect;
	NXRect		*p_rect;

	NXModalSession	theSession;

	if (eraseFields)
	{
		[self  eraseFields:self];
		eraseFields = NO;
	}

	PSsetgray(NX_WHITE);
	NXRectFill(r);

	initGparms(&parms);
	if (drawFrame)
	{
		parms.linewidth = 0.05;
		initUpath(drawUpath);
	}
	else
	{
		if (drawEveryPath)
			p_parms = NULL;
		else
			p_parms = &parms;
	}
	setGparms(&parms);
	NXSetColor(parms.color);
	
	if (drawEverything)
		p_rect = NULL;
	else
	{
		rect = *r;
		rect.origin.x += drawOrigin.x;
		rect.origin.y += drawOrigin.y;
		p_rect = &rect;
	}
	
	tracing = [NXApp  tracing];
	if (tracing)
	{
		[[NXApp redrawButton]  setTitle:"STOP"];

		[NXApp beginModalSession:&theSession for:[NXApp  methodsWindow]];
		DPSTraceContext(DPSGetCurrentContext(), YES);
	}

	PSgsave();
	NXRectClip(r);
	PStranslate(-drawOrigin.x, -drawOrigin.y);
	PSWMarkTime ();  NXPing();
		num = [graphicsListId count];
		for (i = 0; i < num; i++)
		{
			if (tracing && (i % MODALTRACE == 0))
				if ([NXApp runDrawModalSession:&theSession] != NX_RUNCONTINUES)
					break;

			if (drawFrame)
				[self  getUpath:p_rect  fromGraphic:[graphicsListId  objectAt:i]];
			else
				[[graphicsListId  objectAt:i]  drawObject:p_rect  currentParms:p_parms
					withManner:drawManner  timingInfo:&timing_info];
		}

		if (drawFrame && drawCombined)
		{
			if (drawManner == REDBOOK)
				strokeRedBook(&timing_info.num_strokes);
			else
				strokeUpath(drawUpath, drawManner, &timing_info.num_strokes);
		}

	PSWReturnTime (&ElapsedTime);
	PSgrestore();

	if (tracing)
	{
		DPSTraceContext(DPSGetCurrentContext(), NO);
		[NXApp endModalSession:&theSession];

		[[NXApp redrawButton]  setTitle:"redraw"];
	}

	timing_info.drawingtime += ElapsedTime;
	++timing_info.num_draws;

	if (displayFields)
	{
		[self  displayFields:self];
		displayFields = NO;
		eraseFields = YES;
	}

	return self;			
}

@end
